#ifdef SDK_IN_KERNEL

#include <linux/init.h>
#include <asm/types.h>
#include <asm/atomic.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/kthread.h>
#include <linux/audit.h>
#include <net/sock.h>
#include <net/netlink.h>
#include <linux/skbuff.h>
#include <linux/netlink.h>
#include <linux/inotify.h>
#include <linux/freezer.h>
#include <linux/tty.h>
#include <linux/netlink.h>
#include <linux/version.h>
#include "ctc_shell.h"
#include "ctc_shell_server.h"
#include "ctc_cli.h"

ctc_vty_info_t g_vty_client;
ctc_vty_info_t* vty_client[CTC_VTY_CLIENT_NUM] = {NULL};

STATIC int
ctc_vty_sendto(ctc_vti_t* vty, const char *szPtr, const int szPtr_len);
STATIC int
ctc_vty_send_quit(ctc_vti_t* vty);

static struct sock *ctc_master_cli_netlinkfd = NULL;
extern ctc_vti_t*
ctc_vti_create(int mode);

ctc_vty_info_t *
ctc_vty_lookup_client(struct nlmsghdr *msgh)
{
    int client_id = 0;
    int first_free_client = CTC_VTY_CLIENT_NUM;

    do{
        if (!vty_client[client_id])
        {
            first_free_client = (first_free_client == CTC_VTY_CLIENT_NUM ? client_id : first_free_client);
            continue;
        }
        if (vty_client[client_id]->msg_pid == msgh->nlmsg_pid)
        {
            return vty_client[client_id];
        }
    }while(++client_id < CTC_VTY_CLIENT_NUM);

    if (first_free_client != CTC_VTY_CLIENT_NUM)
    {
        if (g_vty_client.msg_pid == 0)
        {
            g_vty_client.msg_pid = msgh->nlmsg_pid;
            return &g_vty_client;
        }
        else
        {
            vty_client[first_free_client] = mem_malloc(MEM_LIBCTCCLI_MODULE, sizeof(ctc_vty_info_t));
            if (!vty_client[first_free_client])
            {
                return &g_vty_client;
            }
            sal_memset(vty_client[first_free_client], 0, sizeof(ctc_vty_info_t));
        }
        
        vty_client[first_free_client]->msg_pid = msgh->nlmsg_pid;
        ctc_vti_init_client(vty_client[first_free_client]);
        if (!vty_client[first_free_client]->matched_desc_ptr)
        {
            vty_client[first_free_client]->matched_desc_ptr = g_vty_client.matched_desc_ptr;
        }
#ifdef ASW_ALONE
        vty_client[first_free_client]->ctc_vti = ctc_vti_create(CTC_ASW_MODE);
#else
        vty_client[first_free_client]->ctc_vti = ctc_vti_create(CTC_SDK_MODE);
#endif
        vty_client[first_free_client]->ctc_vti->client_id = first_free_client;
        vty_client[first_free_client]->ctc_vti->output = g_vty_client.ctc_vti->output;
        ctc_vti_prompt(vty_client[first_free_client]->ctc_vti);
        return vty_client[first_free_client];
    }
    return &g_vty_client;
}

STATIC ctc_vti_t* ctc_vty_lookup_by_pid_errno(unsigned int pid, ctc_vty_info_t* p_vty_client)
{
    if (p_vty_client->ctc_vti->fd != pid)
    {
        p_vty_client->ctc_vti->fd    = pid;
        p_vty_client->ctc_vti->printf = ctc_vty_sendto;
        p_vty_client->ctc_vti->quit   = ctc_vty_send_quit;
#ifdef ASW_ALONE
        p_vty_client->ctc_vti->node   = CTC_ASW_MODE;
#else
        p_vty_client->ctc_vti->node   = CTC_SDK_MODE;
#endif    
        ctc_vti_prompt(p_vty_client->ctc_vti);
    }
    return p_vty_client->ctc_vti;
}

STATIC int ctc_vty_send_quit(ctc_vti_t* vty)
{
    int size;
    struct sk_buff *skb;
    sk_buff_data_t old_tail;
    struct nlmsghdr *nlh;

    int retval;

    if (-1 == vty->fd)
    {
        return -1;
    }

    size = NLMSG_SPACE(CTC_SDK_NETLINK_MSG_LEN);
    skb =  alloc_skb(size, GFP_KERNEL);

	old_tail = skb->tail;
    nlh = nlmsg_put(skb, 0, 0, CTC_SDK_CMD_QUIT, NLMSG_SPACE(0), 0);
    old_tail = skb->tail;

    nlh->nlmsg_len  = skb->tail - old_tail;

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,10))
    NETLINK_CB(skb).portid = 0;
#else
    NETLINK_CB(skb).pid = 0;
#endif
    NETLINK_CB(skb).dst_group = 0;

    retval = netlink_unicast(ctc_master_cli_netlinkfd, skb, vty->fd, MSG_DONTWAIT);

    if(retval < 0)
    {
        ctc_cli_out(KERN_DEBUG "%s:%d netlink_unicast return: %d\n", __FUNCTION__,__LINE__,retval);
    }

    vty->fd = -1;
	ctc_vty_release_client(vty);
    return retval;
}


STATIC int ctc_vty_sendto(ctc_vti_t* vty, const char *szPtr, const int szPtr_len)
{
    int size;
    struct sk_buff *skb;
    sk_buff_data_t old_tail;
    struct nlmsghdr *nlh;

    int retval;

    if (-1 == vty->fd)
    {
        return -1;
    }
    if(0 == szPtr_len)
    {
        return 0;
    }

    size = NLMSG_SPACE(CTC_SDK_NETLINK_MSG_LEN);
    skb =  alloc_skb(size, GFP_KERNEL);

	old_tail = skb->tail;
    nlh = nlmsg_put(skb, 0, 0, 0, NLMSG_SPACE(strlen(szPtr) + 1) - sizeof(struct nlmsghdr), 0);

    old_tail = skb->tail;

    memcpy(NLMSG_DATA(nlh), szPtr, strlen(szPtr) + 1);

    nlh->nlmsg_len  = skb->tail - old_tail;
    nlh->nlmsg_seq  = szPtr_len;

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,10))
    NETLINK_CB(skb).portid = 0;
#else
    NETLINK_CB(skb).pid = 0;
#endif
    NETLINK_CB(skb).dst_group = 0;

    retval = netlink_unicast(ctc_master_cli_netlinkfd, skb, vty->fd, 0);

    if(retval < 0)
    {
        ctc_cli_out(KERN_DEBUG "%s:%d netlink_unicast return: %d\n", __FUNCTION__,__LINE__,retval);
    }

    return retval;
}


STATIC void ctc_vty_recvfrom(struct sk_buff *skb)
{
    ctc_vti_t* vty = NULL;
	ctc_vty_info_t*     p_vty_client = NULL;
    struct nlmsghdr *nlh = NULL;

    if(skb->len >= sizeof(struct nlmsghdr))
    {
        nlh = (struct nlmsghdr *)skb->data;
		p_vty_client = ctc_vty_lookup_client(nlh);
        vty = ctc_vty_lookup_by_pid_errno(nlh->nlmsg_pid, p_vty_client);
        if((nlh->nlmsg_len >= sizeof(struct nlmsghdr))
            && (skb->len >= nlh->nlmsg_len))
        {
            ctc_vti_read_cmd(vty, (char *)NLMSG_DATA(nlh), nlh->nlmsg_seq);
        }
    }
    else
    {
        ctc_cli_out("%s:%d receive error\n",__FUNCTION__,__LINE__);
    }
}


int ctc_vty_socket()
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,10))
    struct netlink_kernel_cfg cfg;
    cfg.groups = 0;
    cfg.flags = 0;
    cfg.input = ctc_vty_recvfrom;
    cfg.cb_mutex = NULL;
    cfg.bind = NULL;
    cfg.unbind = NULL;
    cfg.compare = NULL;
    ctc_master_cli_netlinkfd = netlink_kernel_create(&init_net, CTC_SDK_NETLINK, &cfg);
#else
    ctc_master_cli_netlinkfd = netlink_kernel_create(&init_net, CTC_SDK_NETLINK, 0, ctc_vty_recvfrom, NULL, THIS_MODULE);
#endif
    if(!ctc_master_cli_netlinkfd){
        return -1;
    }

    return 0;
}

void ctc_vty_close()
{
    if (ctc_master_cli_netlinkfd)
    {
        sock_release(ctc_master_cli_netlinkfd->sk_socket);
    }
}
#endif
